package com.lxl.config;

import com.lxl.common.CommonUtil;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.sql.Statement;
import java.util.*;

/**
 * todo 测试
 * sql执行时间记录拦截器
 * @Description
 * @Date 2021/11/28 15:32
 * @Created by LXLDEMO
 */
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "query",args = {Statement.class, ResultHandler.class }),
             @Signature(type = StatementHandler.class, method = "update", args = {Statement.class }),
             @Signature(type = StatementHandler.class, method = "batch", args = {Statement.class })})
public class MybatisLogConfig implements Interceptor {
    private static final Logger log = LoggerFactory.getLogger(MybatisLogConfig.class);


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object target = invocation.getTarget();

        // 获取毫秒数
        long startTime = System.currentTimeMillis();
        StatementHandler statementHandler = (StatementHandler) target;
        try {
            return invocation.proceed();
        }finally {
            // sql执行结束时间
            long endTime = System.currentTimeMillis();
            // sql执行所用时间
            long sqlCost = endTime - startTime;

            BoundSql boundSql = statementHandler.getBoundSql();
            String sql = boundSql.getSql();
            Object parameterObject = boundSql.getParameterObject();
            List<ParameterMapping> parameterMappingList = boundSql.getParameterMappings();

            // 格式化SQL语句，去除换行符，替换参数
            sql = formatSql(sql, parameterObject, parameterMappingList);
//            log.info("SQL: ["+ sql +"]执行耗时[" + sqlCost + "ms]");
            log.info("SQL执行耗时[" + sqlCost + "ms]");

            // 向日志中打印参数
            String parameter = printParameter((Map<String,Object>) parameterObject, parameterMappingList,boundSql);
            if (!CommonUtil.isEmpty(parameter)){
                log.info("SQL执行所需参数==========>:"+parameter);
            }
        }
    }

    /**
     * 打印参数日志
     * @param parameterObject           接收参数集合
     * @param parameterMappingList      sql引用参数集合
     * @param boundSql
     * @return
     */
    private String printParameter(Map<String,Object> parameterObject, List<ParameterMapping> parameterMappingList, BoundSql boundSql){
        StringJoiner resultParam = new StringJoiner(", ", "{", "}");
        if (CommonUtil.isEmpty(parameterObject)){
            return "";
        }
        for (ParameterMapping parameterMapping : parameterMappingList){
            // 参数名称
            String paramName = parameterMapping.getProperty();
            if (parameterObject.containsKey(paramName)){
                resultParam.add(parameterObject.get(paramName).toString());
            }else if (boundSql.hasAdditionalParameter(paramName)){
                resultParam.add(boundSql.getAdditionalParameter(paramName).toString());
            }
        }
        return resultParam.toString();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }

    /**
     * 格式化SQL
     * @param sql
     * @param parameterObject
     * @param parameterMappingList
     * @return
     */
    @SuppressWarnings("unchecked")
    private String formatSql(String sql, Object parameterObject, List<ParameterMapping> parameterMappingList){
        // 输入sql字符串空判断
        if (null ==sql || 0 == sql.length()){
            return "";
        }

        // 美化SQL
        sql = beautifySQL(sql);

        // 不传参数的场景，直接把SQL美化后返回
        if (null == parameterObject || null == parameterMappingList || 0 == parameterMappingList.size()){
            return sql;
        }
        return sql;
    }

    /**
     * 美化sql
     * @param sql
     * @return
     */
    private String beautifySQL(String sql){
        sql = sql.replaceAll("[\\s\n ]+"," ");
        return sql;
    }
}
